package com.crimsec.utils.dinfo;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.HashMap;
import java.util.Properties;
import java.util.Set;

/**
 * $Id$
 */
public class Utils {

    private static HashMap<String, Utils> utilsInstances = new HashMap<String, Utils>(5);
    private Properties props = new Properties();
    private IDefaults idi;

    public static Utils getInstance(IDefaults idi, String name) throws IOException {
        Utils result = utilsInstances.get(name);
        if (result == null) {
            result = new Utils(idi, name);
            utilsInstances.put(name, result);
            return result;
        }
        return result;
    }

    public static Utils getInstance(IDefaults idi) throws IOException {
        return getInstance(idi, idi.getDefPropsName());
    }

    private Utils(IDefaults idi, String name) throws IOException {
        this.idi = idi;
        loadProps(name);
    }

    /**
     * @param other propeties for search namespace
     * @param namespace name (don't use wildchars)
     * @return a new Properties with keys(an their values) who names start with
     * given namesapace
     */
    public static Properties getPropertiesForNamespace(String namespace, Properties other) {
        if (other != null) {
            Properties result = new Properties();
            Set<String> keys = other.stringPropertyNames();
            for (String key : keys) {
                if (key.startsWith(namespace)) {
                    result.put(key, other.getProperty(key));
                }
            }
            return result;
        } else {
            return null;
        }
    }

    public InputStream openStream(String name) throws IOException {
        return openStream(name, this);
    }

    /**
     * Возвращает InputStream в следующем порядке: 1. как URL через параметр
     * 'name' 2. как ресурс через параметр 'name'
     *
     * @param name - url или имя ресурса
     * @param obj - объект, classloader которого, будет использован для загрузки
     * ресурса
     * @return an InputStream
     * @throws IOException
     */
    public static InputStream openStream(String name, Object obj) throws IOException {
        InputStream result;
        try {
            result = openStreamFromUrl(name);
        } catch (IOException e) {
            result = openStreamFromResource(name, obj);
        }
        if (result == null) {
            throw new IOException("can't create an InputStream for:" + name);
        }
        return result;
    }

    public static InputStream openStreamFromUrl(String name) throws IOException {
        try {
            URL url = new URL(name);//TODO: may be append `file://` here, for @name without schema
            return url.openStream();
        } catch (Exception e) {
            throw new IOException(e);
        }
    }

    public static InputStream openStreamFromResource(String name, Object obj) throws IOException {
        return obj.getClass().getClassLoader().getResourceAsStream(name);
    }

    private void loadProps(String name) throws IOException {
        try {
            props.load(new InputStreamReader(openStreamFromUrl(name)));
        } catch (IOException e) {
            try {
                loadPropsFromResource(name);
            } catch (IOException ie) {
                loadPropsFromResource(this.idi.getDefPropsName());
            }
        }
    }

    private void loadPropsFromResource(String resName) throws IOException {
        try {
            InputStream i = openStreamFromResource(resName, this);
            InputStreamReader ir = new InputStreamReader(i);
            props.load(ir);
        } catch (Exception e) {
            throw new IOException(e);
        }
    }

    public String getValue(String propName) {
        String candidate = System.getProperty(propName);
        if (((candidate == null) /*|| (candidate.isEmpty())*/)) {
            candidate = props.getProperty(propName);
        }
        if ((candidate == null) || (candidate.isEmpty())) {
            candidate = idi.getDefaults().get(propName);
        }
        return candidate;
    }

    public boolean checkProp(String name) {
        String v = getValue(name);
        return ((v) != null) && (!v.isEmpty());
    }
}
